package com.harboursoftware.xstorage.operations;

import java.io.InputStream;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.math.LongRange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ListMultimap;
import com.harboursoftware.xstorage.XStorageException;
import com.harboursoftware.xstorage.ac.AccessControlPolicy;
import com.harboursoftware.xstorage.ac.Permission;
import com.harboursoftware.xstorage.fs.BucketNotExistException;
import com.harboursoftware.xstorage.fs.ObjectNotExistException;
import com.harboursoftware.xstorage.io.RangeInputStream;
import com.harboursoftware.xstorage.model.XElement;
import com.harboursoftware.xstorage.model.XObject;
import com.harboursoftware.xstorage.model.XUser;
import com.harboursoftware.xstorage.results.EmptyContentResult;
import com.harboursoftware.xstorage.results.ErrorResult;
import com.harboursoftware.xstorage.results.GetObjectResult;
import com.harboursoftware.xstorage.results.Result;
import com.harboursoftware.xstorage.util.DateParseException;
import com.harboursoftware.xstorage.util.DateUtils;
import com.harboursoftware.xstorage.util.MultimapUtils;

public class GetObject extends Operation {
    private final static Logger log = LoggerFactory.getLogger(GetObject.class);

    @Override
    public Result execute(XUser requester, XElement element, ListMultimap<String, String> headers,
        Map<String, String> parameters) {
        log.debug("execute");
        XObject object = (XObject) element;
        // TODO: 处理Request Headers： Range，If-Modified-Since...
        // TODO: 输出metadata 
        // TODO:输出头：content-length， content-type
        // TODO:len和修改时间可以从 FileStatusk
        try {
            fileSystem.loadObject(object);
        } catch (BucketNotExistException e) {
            return ErrorResult.create(ErrorResult.Type.NO_SUCH_BUCKET);
        } catch (ObjectNotExistException e) {
            return ErrorResult.create(ErrorResult.Type.NO_SUCH_KEY);
        } catch (XStorageException e) {
            log.error("error occured when get object", e);
            return ErrorResult.create(ErrorResult.Type.INTERNAL_ERROR);
        }

        AccessControlPolicy acp = object.getAcp();
        if (!acp.checkPermission(requester, getRequiredPermission())) {
            //TODO: 未完成
        }
        
        EmptyContentResult emptyContentResult;
        try {
            emptyContentResult = handleRequestHeaders(headers, object);
        } catch (XStorageException e) {
            log.error("error occured when get object", e);
            return ErrorResult.create(ErrorResult.Type.INTERNAL_ERROR);
        }
        
        if(emptyContentResult != null) {
            IOUtils.closeQuietly(object.getInputStream());
            return emptyContentResult;
        }
            
        
        InputStream objectInputStream = null;
        
        LongRange range = getRange(headers);
        if(range != null) {
            objectInputStream = new RangeInputStream(object.getInputStream(), range);
        } else {
            objectInputStream = object.getInputStream();
        }
         
        GetObjectResult getObjectResult = new GetObjectResult(objectInputStream);
        getObjectResult.addAllHeaders(object.getHeaders());
        return getObjectResult;
    }
    
    private EmptyContentResult handleRequestHeaders(ListMultimap<String, String> headers, XObject object) throws XStorageException {
        long lastModified = object.getLastModified();
        //TODO: 重构
        String ifModifiedSinceHeader = MultimapUtils.getFirstValue(headers, "If-Modified-Since");
        if(ifModifiedSinceHeader != null) {
            long ifModifiedSince = 0;
            try {
                ifModifiedSince = DateUtils.parseDate(ifModifiedSinceHeader).getTime();
            } catch (DateParseException e) {
                // TODO 异常处理
                e.printStackTrace();
            }
            if(ifModifiedSince > lastModified) {
                return new EmptyContentResult(304);
            }
        }
        String ifUnmodifiedSinceHeader = MultimapUtils.getFirstValue(headers, "If-Unmodified-Since");
        if(ifUnmodifiedSinceHeader != null) {
            long ifUnmodifiedSince = 0;
            try {
                ifUnmodifiedSince = DateUtils.parseDate(ifUnmodifiedSinceHeader).getTime();
            } catch (DateParseException e) {
             // TODO 异常处理， 是否对date这些数据进行集中处理
                e.printStackTrace();
            }
            if(ifUnmodifiedSince < lastModified) {
                return new EmptyContentResult(412);
            }
            
        }
        
        String ifMatchHeader = MultimapUtils.getFirstValue(headers, "If-Match");
        String ifNoneMatchHeader = MultimapUtils.getFirstValue(headers, "If-None-Match");
        if(ifMatchHeader != null || ifNoneMatchHeader != null) {
            try {
                fileSystem.loadObjectEtag(object);
            } catch (BucketNotExistException e) {
                //ignore
            } catch (ObjectNotExistException e) {
                //ignore
            }
            String etag = object.getEtag();
            if(ifMatchHeader != null && !ifMatchHeader.equals(etag)) {
                return new EmptyContentResult(412);
            }
            
            if(ifNoneMatchHeader != null && ifNoneMatchHeader.equals(etag)) {
                return new EmptyContentResult(304);
            }
        }
        return null;
    }
    
    
    private LongRange getRange(ListMultimap<String, String> headers) {
        String rangeHead = MultimapUtils.getFirstValue(headers, "Range");
        //TODO : 未完成
        return null;
    }

    @Override
    public Permission getRequiredPermission() {
        return Permission.READ;
    }

}
